#!/usr/bin/python3

import os
import glob
import diffSymbolDB
import subprocess
import random
currentDir=os.getcwd()+'/vsroot'

usrlib='/usr/lib'
usrlib64='/usr/lib64'
lib='/lib'
lib64='/lib64'
usrbin = '/usr/bin'
usrsbin = '/usr/sbin'
bin = '/bin'
sbin = '/sbin'
osversion=''

def setOsVersion(version):
    global osversion
    osversion=version

def filterRpmName(rpmname):
    return ".x86_64.rpm" in rpmname or ".noarch.rpm" in rpmname

def get_rpm_name(rpm_file):
    """
    获取 RPM 文件的 Name 字段。
    """
    try:
        result = subprocess.run(
            ['rpm', '-qpi', rpm_file],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True
        )
        if result.returncode != 0:
            print(f"Error: {result.stderr.strip()}")
            return ""
        for line in result.stdout.splitlines():
            if line.startswith("Name"):
                return line.split(":", 1)[1].strip()  
        print("Name field not found in RPM metadata.")
        return ""
    except Exception as e:
        print(f"Exception occurred: {e}")
        return ""

def decompressionRpmFile(rpmpath):
    cmd='rpm2cpio {} | cpio -div -D {}'.format(rpmpath,currentDir)
    os.system(cmd)

def execCmd(cmd):
    process = subprocess.Popen([cmd],shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = process.communicate()

    strResult = stdout.strip().decode('utf-8') if hasattr(stdout, 'decode') else stdout.strip() 
    return strResult

def scanVSrootDir(directory):
    files = []
    if not os.path.exists(directory):
        return files
    for item in os.listdir(directory):  
        full_path = os.path.join(directory, item)  
        if not os.path.isdir(full_path): 
            files.append(item) 
    return files 
 


def collectElfFileInfo(path, rpmpackagename, rpmname, osversion):
    """
    收集 ELF 文件的信息
    """
    print("csan dir {}{}".format(currentDir,path))
    filelist=scanVSrootDir('{}{}'.format(currentDir,path))
    print("遍历的文件filelist {}".format(filelist))
    if not filelist:
        return
    for file in filelist:
        full_path = os.path.join(currentDir.rstrip('/'), path.lstrip('/'), file)
        print("当前分析的file： {}".format(full_path))
        file_type = execCmd("file -b {}".format(full_path))
        print("当前分析的file_type： {}".format(file_type))
        if "ELF" in file_type:  
            diffSymbolDB.insertLibSymbolDiffValue2Table(path+'/'+file, rpmpackagename, rpmname, osversion)

def dailfileinsert2DB(path,rpmpackagename,rpmname,osversion):
    print("csan dir {}{}".format(currentDir,path))
    filelist=scanVSrootDir('{}{}'.format(currentDir,path))
    print("遍历的文件filelist {}".format(filelist))
    for file in filelist:
        linkfile=currentDir+path+'/'+file
        is_link=0
        if os.path.islink(currentDir+path+'/'+file):
            linkfile=os.path.realpath(currentDir+path+'/'+file)
            print("真的linkfile {}".format(linkfile))
            is_link=1
        print("insert path {} {}".format(path,linkfile[len(currentDir):]))
        diffSymbolDB.insertLibRpmPakcageRelevanceValue2Table(path+'/'+file,rpmpackagename,is_link,linkfile[len(currentDir):],rpmname,osversion)


def handleOtherFiles(item, rpmfile, rpmname, osversion, log_file="./missing_files.log"):
    """
    处理解压后的 RPM 包，使用 `rpm -qpl` 获取 RPM 包的文件列表。
    如果没有文件列表，将错误信息统一保存到一个日志文件。
    插入数据库时使用相对路径。
    """
    try:
        # 使用 rpm -qpl 获取文件列表
        result = subprocess.run(
            ["rpm", "-qpl", item],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True,  # 等效于 universal_newlines=True
            check=True
        )
        
        file_list = result.stdout.splitlines()       
        if not file_list:
            with open(log_file, "a") as f:  
                f.write(f"No files found in RPM package: {item}\n")
            print(f"Logged missing files for RPM package: {item}")
            return
        selected_file = random.choice(file_list)
        diffSymbolDB.insertOtherFilesToTable(selected_file, rpmfile, rpmname, osversion)
        print(f"Inserted random file: {selected_file}")
    except Exception as e:
        print(f"Unexpected error while handling {item}: {e}")




def caleSoLibCount(rpmpath):
    """
    检查 RPM 包中是否包含 .so 文件或 ELF 文件。
    返回 True 如果包含 .so 或 ELF 文件，反之返回 False。
    """
    libsize = execCmd("""rpm -qpl {} | grep ^/usr/lib/[^/]*\.so | wc -l""".format(rpmpath))
    lib64size = execCmd("""rpm -qpl {} | grep ^/usr/lib64/[^/]*\.so | wc -l""".format(rpmpath))
    libEx = execCmd("""rpm -qpl {} | grep ^/lib/[^/]*\.so | wc -l""".format(rpmpath))
    lib64Ex = execCmd("""rpm -qpl {} | grep ^/lib64/[^/]*\.so | wc -l""".format(rpmpath))

    paths_to_check = [
        currentDir + "/usr/bin/",
        currentDir + "/usr/sbin/",
        currentDir + "/bin/",
        currentDir + "/sbin/"
    ]

    elf_files_count = 0

    for path in paths_to_check:
        # 确保路径存在
        if os.path.exists(path):
            for file in os.listdir(path):
                file_path = os.path.join(path, file)
                if os.path.isfile(file_path):  
                    
                    file_type = execCmd("file -b {}".format(file_path))
                    if "ELF" in file_type:  
                        elf_files_count += 1
    print(f"libsize: {libsize}, lib64size: {lib64size}, libEx: {libEx}, lib64Ex: {lib64Ex}, elf_files: {elf_files_count}")   
    # 如果有任何 .so 文件或 ELF 文件，返回 True
    if int(libsize) > 0 or int(lib64size) > 0 or int(libEx) > 0 or int(lib64Ex) > 0 or elf_files_count > 0:
        return True
    else:
        return False


def parseRpmFileInfo(rpmlist):
    for item in rpmlist:
        rpmname = get_rpm_name(item)
        print(item)
        decompressionRpmFile(item)
        has_so_or_elf = caleSoLibCount(item)
        rpmfile = os.path.basename(item)
        print("当前分析的rpmfile： {}".format(rpmfile))  
        if has_so_or_elf:
            dailfileinsert2DB(usrlib64, rpmfile, rpmname, osversion)
            dailfileinsert2DB(usrlib, rpmfile, rpmname, osversion)
            dailfileinsert2DB(lib64, rpmfile, rpmname, osversion)
            dailfileinsert2DB(lib, rpmfile, rpmname, osversion)
            collectElfFileInfo(usrbin, rpmfile, rpmname, osversion)
            collectElfFileInfo(usrsbin, rpmfile, rpmname, osversion)
            collectElfFileInfo(bin, rpmfile, rpmname, osversion)
            collectElfFileInfo(sbin, rpmfile, rpmname, osversion)
        else:
            handleOtherFiles(item, rpmfile, rpmname, osversion)        
        os.system('rm -rf {}/*'.format(currentDir))
        
            
            
            

def scanBaseOSDir(path):
    filelist=glob.glob(path)
    return list(filter(filterRpmName,filelist))


def analysisRpmPackageFiles(BaseOSpath):
    
    diffSymbolDB.createTable()
    rpmlist=scanBaseOSDir(BaseOSpath)
    print("当前扫描的rpmlist {}".format(rpmlist))
    parseRpmFileInfo(rpmlist)
    
if __name__ == "__main__":
    setOsVersion('cq24')
    analysisRpmPackageFiles('/root/work/abi/rpmlist/*.rpm')